﻿using System;
using System.Collections.Generic;
using System.Xml.Linq;
using System.Reflection;

using AnB.Common;

namespace AnB.Net
{
    public class Dispatcher : Packet.ICmdHelper
    {
        public Dispatcher(Type cmdHandlerClass, params object[] cmdHandlerArgs)
        {
            mCmdHandlerClass = cmdHandlerClass;
            mCmdHandlerArgs = cmdHandlerArgs;
        }
        private Type mCmdHandlerClass;
        private object[] mCmdHandlerArgs;
        public CmdHandler GetCmdHandler(ushort cmd)
        {
            CmdHandler handler;
            if (mHandlersByCmd.TryGetValue(cmd, out handler))
            {
                return handler;
            }
            throw new Exception(string.Format("failed to get cmd handler of 0X{0:X4}", cmd));
        }
        public CmdHandler GetCmdHandler(string cmd)
        {
            CmdHandler handler;
            if (mHandlersByName.TryGetValue(cmd, out handler))
            {
                return handler;
            }
            throw new Exception(string.Format("failed to get cmd handler of {0}", cmd));
        }

        private List<CmdHandler> mHandlers = new List<CmdHandler>();
        private Dictionary<ushort, CmdHandler> mHandlersByCmd = new Dictionary<ushort, CmdHandler>();
        private Dictionary<string, CmdHandler> mHandlersByName = new Dictionary<string, CmdHandler>();

        protected void AddHandler(CmdHandler handler)
        {
            mHandlers.Add(handler);
            mHandlersByCmd[handler.Cmd] = handler;
            mHandlersByName[handler.Name] = handler;
        }

        public void ClearHandlers()
        {
            mHandlers.Clear();
            mHandlersByCmd.Clear();
            mHandlersByName.Clear();
        }

        private long mSizeLimit;
        public long SizeLimit
        {
            get
            {
                return mSizeLimit;
            }
        }

        public void Load(string xmlfile, IEnumerable<String> namespaces)
        {
            var xml = XElement.Load(xmlfile);
            ClearHandlers();
            mSizeLimit = Utils.ParseBinary((string)xml.Attribute("size-limit"));

            foreach (var cmd in xml.Elements("cmd"))
            {
                var cmdHandler = (CmdHandler)Activator.CreateInstance(mCmdHandlerClass, mCmdHandlerArgs);
                string id = (string)cmd.Attribute("id");
                string name = (string)cmd.Attribute("name");
                string sizeLimit = (string)cmd.Attribute("size-limit");

                cmdHandler.Cmd  = ushort.Parse(id, System.Globalization.NumberStyles.HexNumber);
                cmdHandler.Name = name;
                cmdHandler.Description = string.Format("{0}|0X{1}", name, id);
                cmdHandler.SizeLimit = string.IsNullOrEmpty(sizeLimit) ? mSizeLimit : Utils.ParseBinary(sizeLimit);

                string in_  = (string)cmd.Attribute("in");
                cmdHandler.In  = Utils.GetType(in_,  namespaces);
                if (cmdHandler.In == null)
                {
                    throw new Exception(string.Format("{0} failed get type of '{1}'", cmdHandler.Description, in_));
                }
                string out_ = (string)cmd.Attribute("out");
                cmdHandler.Out = Utils.GetType(out_, namespaces);
                if (cmdHandler.Out == null)
                {
                    throw new Exception(string.Format("{0} failed get type of '{1}'", cmdHandler.Description, out_));
                }

                cmdHandler.InBody  = Packet.GetBodyType(cmdHandler.In);
                cmdHandler.OutBody = Packet.GetBodyType(cmdHandler.Out);
                cmdHandler.Prepare();

                AddHandler(cmdHandler);
            }
        }
    }
}
